#include "GxCamera.h"

bool AcquisitionFlag = true;

GxCamera::GxCamera()
{
    g_hDevice = NULL;
    g_pDeviceSN = "KE0210010002";
}

GxCamera::~GxCamera()
{

}

GX_STATUS GxCamera::initLib()
{
    GX_STATUS status = GX_STATUS_SUCCESS;
    printf("\n");
    printf("-------------------------------------------------------------\n");
    printf("You've entered camera function which acquire color image continuously and show it through opencv");
    printf("-------------------------------------------------------------\n");
    printf("\n");
    printf("Initializing......");
    printf("\n");
    status = GXInitLib();
    if (status == GX_STATUS_SUCCESS)
    {
        printf("Initialize succeeded ! \n");
    }
    else
    {
        printf("Initialize failed ! \n");
        exit(status);
    }
}

GX_STATUS GxCamera::OpenDevice()
{
    GX_STATUS status = GX_STATUS_SUCCESS;
    uint32_t DeviceNum = 0;
    //g_pDeviceSN = const_cast<char*>(CameraSN);

    status = GXUpdateDeviceList(&DeviceNum, 1000);
    if (DeviceNum <= 0)
    {
        printf("<No device found>\n");
        GXCloseLib();
        exit(status);
    }

    GX_OPEN_PARAM stOpenParam;
    stOpenParam.accessMode = GX_ACCESS_EXCLUSIVE;
    stOpenParam.openMode = GX_OPEN_SN;
    stOpenParam.pszContent = g_pDeviceSN;

    status = GXOpenDevice(&stOpenParam, &g_hDevice);
    if (status == GX_STATUS_SUCCESS)
    {
        printf("Open Device succeeded !\n");
    }
    else
    {
        printf("Open Device failed !\n");
        exit(status);
    }
}

void GxCamera::setExposureGainParam(bool AutoExposure, bool AutoGain, double ExposureTime, double AutoExposureTimeMin, double AutoExposureTimeMax, double Gain, double AutoGainMin, double AutoGainMax, int64_t GrayValue)
{
    exposure_gain.m_bAutoExposure = AutoExposure;
    exposure_gain.m_bAutoGain = AutoGain;
    exposure_gain.m_dExposureTime = ExposureTime;
    exposure_gain.m_dAutoExposureTimeMin = AutoExposureTimeMin;
    exposure_gain.m_dAutoExposureTimeMax = AutoExposureTimeMax;
    exposure_gain.m_dGain = Gain;
    exposure_gain.m_dAutoGainMin = AutoGainMin;
    exposure_gain.m_dAutoGainMax = AutoGainMax;
    exposure_gain.m_i64GrayValue = GrayValue;
}

GX_STATUS GxCamera::setExposureGain()
{
    GX_STATUS status;

    // Set Exposure
    if(exposure_gain.m_bAutoExposure)
    {
        status = GXSetEnum(g_hDevice,GX_ENUM_EXPOSURE_AUTO,GX_EXPOSURE_AUTO_CONTINUOUS);

        status = GXSetFloat(g_hDevice,GX_FLOAT_AUTO_EXPOSURE_TIME_MAX,exposure_gain.m_dAutoExposureTimeMax);

        status = GXSetFloat(g_hDevice,GX_FLOAT_AUTO_EXPOSURE_TIME_MIN,exposure_gain.m_dAutoExposureTimeMin);

    }
    else
    {
        status = GXSetEnum(g_hDevice,GX_ENUM_EXPOSURE_MODE,GX_EXPOSURE_MODE_TIMED);

        status = GXSetEnum(g_hDevice,GX_ENUM_EXPOSURE_AUTO,GX_EXPOSURE_AUTO_OFF);

        status = GXSetFloat(g_hDevice,GX_FLOAT_EXPOSURE_TIME,exposure_gain.m_dExposureTime);

    }

    // Set Gain
    if(exposure_gain.m_bAutoGain)
    {
        status = GXSetEnum(g_hDevice,GX_ENUM_GAIN_AUTO,GX_GAIN_AUTO_CONTINUOUS);

        status = GXSetFloat(g_hDevice,GX_FLOAT_AUTO_GAIN_MAX,exposure_gain.m_dAutoGainMax);

        status = GXSetFloat(g_hDevice,GX_FLOAT_AUTO_GAIN_MIN,exposure_gain.m_dAutoGainMin);

    }
    else
    {
        status = GXSetEnum(g_hDevice,GX_ENUM_GAIN_AUTO,GX_GAIN_AUTO_OFF);

        status = GXSetEnum(g_hDevice,GX_ENUM_GAIN_SELECTOR,GX_GAIN_SELECTOR_ALL);

        status = GXSetFloat(g_hDevice,GX_FLOAT_GAIN,exposure_gain.m_dGain);

    }

    // Set Expected Gray Value
    status = GXSetInt(g_hDevice,GX_INT_GRAY_VALUE,exposure_gain.m_i64GrayValue);

    return GX_STATUS_SUCCESS;
}

void GxCamera::setRoiParam(int64_t Width, int64_t Height, int64_t OffsetX, int64_t OffsetY)
{
    roi.m_i64Width = Width;
    roi.m_i64Height = Height;
    roi.m_i64OffsetX = OffsetX;
    roi.m_i64OffsetY = OffsetY;
}

GX_STATUS GxCamera::setRoi()
{
    GX_STATUS status = GX_STATUS_SUCCESS;
    //设 置 一 个 offset 偏 移 为 (X,Y) ,Width x Height 尺 寸 的 区 域
    status = GXSetInt(g_hDevice, GX_INT_OFFSET_X, roi.m_i64OffsetX);
    status = GXSetInt(g_hDevice, GX_INT_OFFSET_Y, roi.m_i64OffsetY);
    status = GXSetInt(g_hDevice, GX_INT_WIDTH, roi.m_i64Width);
    status = GXSetInt(g_hDevice, GX_INT_HEIGHT, roi.m_i64Height);
}

void GxCamera::setWhiteBalanceParam(bool WhiteBalanceOn, GX_AWB_LAMP_HOUSE_ENTRY lightSource)
{
    //自动白平衡光照环境
    // GX_AWB_LAMP_HOUSE_ADAPTIVE 自适应
    // GX_AWB_LAMP_HOUSE_FLUORESCENCE 荧光灯
    // GX_AWB_LAMP_HOUSE_INCANDESCENT 白炽灯
    // GX_AWB_LAMP_HOUSE_U30 光源温度3000k
    // GX_AWB_LAMP_HOUSE_D50 光源温度5000k
    // GX_AWB_LAMP_HOUSE_D65 光源温度6500k
    // GX_AWB_LAMP_HOUSE_D70 光源温度7000k
    white_balance.m_bWhiteBalance = WhiteBalanceOn;
    white_balance.lightSource = lightSource;
}

GX_STATUS GxCamera::setWhiteBalance()
{
    //选择白平衡通道
    GX_STATUS status;

    if(white_balance.m_bWhiteBalance)
    {
        //status = GXSetEnum(g_hDevice, GX_ENUM_BALANCE_RATIO_SELECTOR, GX_BALANCE_RATIO_SELECTOR_RED);
        //status = GXSetEnum(g_hDevice, GX_ENUM_BALANCE_RATIO_SELECTOR, GX_BALANCE_RATIO_SELECTOR_GREEN);
        status = GXSetEnum(g_hDevice, GX_ENUM_BALANCE_RATIO_SELECTOR, GX_BALANCE_RATIO_SELECTOR_BLUE);

        //设置自动白平衡感兴趣区域(整个roi)
        status = GXSetInt(g_hDevice, GX_INT_AWBROI_WIDTH, roi.m_i64Width);
        status = GXSetInt(g_hDevice, GX_INT_AWBROI_HEIGHT, roi.m_i64Height);
        status = GXSetInt(g_hDevice, GX_INT_AWBROI_OFFSETX, 0);
        status = GXSetInt(g_hDevice, GX_INT_AWBROI_OFFSETY, 0);

        //自动白平衡设置
        status = GXSetEnum(g_hDevice, GX_ENUM_AWB_LAMP_HOUSE, white_balance.lightSource);

        //设置连续自动白平衡
        status = GXSetEnum(g_hDevice, GX_ENUM_BALANCE_WHITE_AUTO, GX_BALANCE_WHITE_AUTO_CONTINUOUS);
    }
    else
    {
        status = GXSetEnum(g_hDevice,GX_ENUM_BALANCE_WHITE_AUTO,GX_BALANCE_WHITE_AUTO_OFF);
    }

    return GX_STATUS_SUCCESS;
}

void GxCamera::setWidthHeightParam(int64_t m_Width, int64_t m_Height)
{
    width_height.Width_MAX = m_Width;

    width_height.Height_MAX = m_Height;
}

GX_STATUS GxCamera::setWidthHeight()
{
    GX_STATUS status;

    status = GXSetInt(g_hDevice, GX_INT_WIDTH_MAX, width_height.Width_MAX);

    status = GXSetInt(g_hDevice, GX_INT_HEIGHT_MAX, width_height.Height_MAX);
}

GX_STATUS GxCamera::AcquisitionStart()
{
    GX_STATUS status;

    //status = setWidthHeight();
    status = setRoi();
    status = setExposureGain();
    status = setWhiteBalance();

    //set acquisition mode
    status = GXSetEnum(g_hDevice, GX_ENUM_ACQUISITION_MODE, GX_ACQ_MODE_CONTINUOUS);
    //set trigger mode
    status = GXSetEnum(g_hDevice, GX_ENUM_TRIGGER_MODE, GX_TRIGGER_MODE_OFF);
    //set buffer quantity of acquisition queue
    uint64_t nBufferNum = ACQ_BUFFER_NUM;
    status = GXSetAcqusitionBufferNumber(g_hDevice, nBufferNum);

    bool bStreamTransferSize = false;
    status = GXIsImplemented(g_hDevice, GX_DS_INT_STREAM_TRANSFER_SIZE, &bStreamTransferSize);

    if(bStreamTransferSize)
    {
        //Set size of data transfer block
        status = GXSetInt(g_hDevice, GX_DS_INT_STREAM_TRANSFER_SIZE, ACQ_TRANSFER_SIZE);
    }

    bool bStreamTransferNumberUrb = false;
    status = GXIsImplemented(g_hDevice, GX_DS_INT_STREAM_TRANSFER_NUMBER_URB, &bStreamTransferNumberUrb);

    if(bStreamTransferNumberUrb)
    {
        //Set qty. of data transfer block
        status = GXSetInt(g_hDevice, GX_DS_INT_STREAM_TRANSFER_NUMBER_URB, ACQ_TRANSFER_NUMBER_URB);
    }

    status = GXStreamOn(g_hDevice);
    if (status != GX_STATUS_SUCCESS)
    {
        printf("GXStreamOn failed ! \n");
        exit(status);
    }
    else
    {
        printf("Gxstream On ! \n");
        PGX_FRAME_BUFFER pFrameBuffer = NULL;
        while(AcquisitionFlag)
        {
            status = GXDQBuf(g_hDevice, &pFrameBuffer, 1000);
            if (pFrameBuffer->nStatus == GX_FRAME_STATUS_SUCCESS)
            {
                //cout << "Image acquisition succeeded..." << endl;
                //printf("Image processing ...\n");
                cv::Mat src;
                src.create(pFrameBuffer->nHeight, pFrameBuffer->nWidth, CV_8UC3);
                uchar* pBGRBuf = NULL;
                pBGRBuf = new uchar[pFrameBuffer->nHeight * pFrameBuffer->nWidth * 3];

                //Convert raw8(bayer) image into BGR24 image
                VxInt32 emDXStatus = DX_OK;
                emDXStatus = DxRaw8toRGB24((unsigned char*)pFrameBuffer->pImgBuf, pBGRBuf, pFrameBuffer->nWidth, pFrameBuffer->nHeight,
                                            RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(BAYERBG), false);
                if(emDXStatus != DX_OK)
                {
                    printf("DxRaw8toRGB24 Failed... \n");
                    delete[] pBGRBuf;
                    pBGRBuf = NULL;
                    continue;
                }
                else
                {
                    //cout << "copy to Mat..." << endl;
                    memcpy(src.data, pBGRBuf, pFrameBuffer->nHeight*pFrameBuffer->nWidth*3);
                    cv::imshow("image", src);
                    delete[] pBGRBuf;
                    pBGRBuf = NULL;
                    char chKey;
                    chKey = cv::waitKey(10);
                    if (chKey == 'x')
                    {
                        break;
                    }
                }
                status = GXQBuf(g_hDevice, pFrameBuffer);
            }
        }
        printf("exit while loop...\n");
    }
    status = GXStreamOff(g_hDevice);
    printf("Stream Off ...\n");
    status = GXCloseDevice(g_hDevice);
    printf("Close Device...\n");
    status = GXCloseLib();
    printf("Close Lib\n");

}
